package com.github.cyf.util.image;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;

/**
 * @author chenyifan
 * @create 2024-10-04 23:03
 */
public class CharImageUtil {

    private static String replaceString = "$@B%8&WM#*oahkbmZO0QJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. ";

    /**
     * 批量转换图片为字符图片
     * @param imageDir 图片目录
     * @param saveDir 保存目录
     * @param isGray 字符是否为黑颜色
     */
    public static void batchToCharImage(String imageDir, String saveDir, boolean isGray) {
        File dir = new File(imageDir);
        if (!dir.exists()) {
            throw new RuntimeException("图片目录不存在");
        }
        for (File file : dir.listFiles()) {
            toCharImage(file.getPath(), Paths.get(saveDir, file.getName()).toString(), isGray);
        }
    }

    /**
     * 图片转字符图片
     * @param imagePath 图片路径
     * @param savePath 保存路径
     * @param isGray 字符是否为黑颜色
     */
    public static void toCharImage(String imagePath, String savePath, boolean isGray)  {
        BufferedImage bi = null;
        try {
            bi = ImageIO.read(new File(imagePath));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        int width = bi.getWidth();
        int height = bi.getHeight();
        int minX = bi.getMinX();
        int minY = bi.getMinY();

        // 创建和源图片大小一样的图片，后面使用画笔往里面填充字符
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        int[] rate = getImageScaledRate(width, height);
        Graphics g = createGraphics(image, width, height, rate[0]);
        for (int y = minY; y < height; y += rate[1]) {
            for (int x = minX; x < width; x += rate[0]) {
                // 像素点RBG值获取
                int pixel = bi.getRGB(x, y);
                int red = (pixel & 0xff0000) >> 16;
                int green = (pixel & 0xff00) >> 8;
                int blue = pixel & 0xff;
                // 灰度计算公式
                float gray = 0.299f * red + 0.587f * green + 0.114f * blue;
                int index = Math.round(gray * (replaceString.length() + 1) / 255);
                // 将灰度值转成字符
                String pixelChar = index >= replaceString.length() ? " " : String.valueOf(replaceString.charAt(index));
                if (!isGray) {
                    g.setColor(new Color(red, green, blue));
                }
                g.drawString(pixelChar, x, y);
            }
        }
        g.dispose();

        // 保存图片
        File file = new File(savePath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        try(FileOutputStream fos = new FileOutputStream(file)) {
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(fos);
            encoder.encode(image);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取画笔，设置一些默认参数
     * @param image 图片
     * @param width 图片宽
     * @param height 图片高
     * @param size 字体大小
     * @return
     */
    private static Graphics createGraphics(BufferedImage image, int width, int height, int size) {
        // 获得画笔
        Graphics g = image.createGraphics();
        // 在指定的矩形区域内填充颜色，默认黑色
        g.fillRect(0, 0, width, height);
        // 设置画笔颜色
        g.setColor(Color.BLACK);
        // 设置字体
        g.setFont(new Font("微软雅黑", Font.PLAIN, size));
        return g;
    }

    /**
     * 简单计算图片长宽缩放比例
     * @param width
     * @param height
     * @return
     */
    private static int[] getImageScaledRate(int width, int height) {
        // 计算比例
        double rate = (double) Math.max(width, height) / Math.min(width, height);

        // 获取整数部分和小数部分
        int intPart = (int) rate;
        double fractionalPart = rate - intPart;

        int x, y;
        // 小数部分靠近0.5，四舍五入后乘以2
        if (Math.abs(fractionalPart - 0.5) < 0.25) {
            x = (int) Math.round(rate * 2);
            y = 2;
        } else {
            // 小数部分不靠近0.5，直接取整数部分
            x = (int) Math.round(rate);
            y = 1;
        }
        return width > height ? new int[]{x, y} : new int[]{y, x};
    }

}
